package brainbots;
import java.util.*;

import brainbots.util.BBUtil;

/**An isntance of a world cell, used to organize fast access by coordinate*/
public class WorldCells<LocatedItem extends Located>{
    
    private ArrayList<LocatedItem>[] cells;
    private ArrayList<LocatedItem> unaligned;
    private double[] cellBordersX;
    private double[] cellBordersY;
    
    private double cellW, cellH, cellx0, celly0, cellx1, celly1;
    private int height, width;
    
    public WorldCells(double x0, double y0, double x1, double y1, int w, int h)
    {
        init(x0,y0,x1,y1,w,h);
    }
    
    
    private void init(double x0, double y0, double x1, double y1, int w, int h)
    {
        height = h;
        width = w;

        cellW = (x1-x0)/w;
        cellH = (y1-y0)/h;
        
        cellx0 = x0;
        celly0 = y0;
        cellx1 = x1;
        celly1 = y1;
        
        cellBordersX = new double[w+1];
        BBUtil.linspace(cellBordersX, x0, x1);
        cellBordersY = new double[h+1];
        BBUtil.linspace(cellBordersY, y0, y1);
        
        //create cells
        cells = new ArrayList [w*h];
        for (int i =0;i<cells.length; ++i ){
            cells[i] = new ArrayList<LocatedItem>();
        }
        
        unaligned = new ArrayList<LocatedItem>();
    }
    
    /**get cell contents*/
    private ArrayList<LocatedItem> get(int x, int y){
        if (x<0 || x>=width || y<0 || y>=height)
            return unaligned;
            //throw new RuntimeException("Non-existent cell coordinates");
        int idx = x + y*width;
        return cells[idx];
    }
    
    /**Update position of all bots in their cells*/
    public void updateBots(){
        int idx = 0;
        for (int y = 0; y<height; ++y){
            for (int x = 0; x<width; ++x,++idx){
                updateCell(cells[idx], x, y);                
            }
        }

        //update bots in the outside cells
        for(int i = 0; i<unaligned.size(); ++i){
            LocatedItem bot = unaligned.get(i);
            double bx = bot.getX();
            double by = bot.getY();
            if (bx >=cellx0 && bx <cellx1 && by>=celly0 && by <celly1){
                unaligned.remove(i);
                --i;
                putItem(bot);
            }
        }
    }
    
    private void updateCell(ArrayList<LocatedItem> cell, int x, int y){
        for(int i =0;i<cell.size();++i){
            LocatedItem bot = cell.get(i);
            double bx = bot.getX();
            double by = bot.getY();
            if (!(bx>=cellBordersX[x] && bx<=cellBordersX[x+1] &&
                by>=cellBordersY[y] && by<=cellBordersY[y+1])){//if bot in in the cell
                cell.remove(i);//remove bot from current cell
                putItem(bot);//and put it back, somewhere
            }
        }
    }
    
    /**Put the bot to the right cell*/
    public void putItem(LocatedItem b){
        int x = idxX(b.getX()); //coordinates of the cell
        int y = idxY(b.getY());
        
        if (x<0 || x>=width || y<0 || y>=height){
            unaligned.add(b);
        }else{
            get(x,y).add(b);
        }
    }
    
 
    //Horizontal index of cell
    private final int idxX(double x){
        return (int)(Math.floor(x-cellx0)/cellW);
    }
    
    private final int idxY(double y){
        return (int)(Math.floor(y-celly0)/cellH);
    }
    
    /**Enumerate all bots in the given circle*/
    public void inCircle(double cx, double cy, double r, ItemEnumerator<LocatedItem> callback)
    {
        double r2 = r*r;
        
        int x0 = Math.min(Math.max(idxX(cx-r),0), width-1);
        int x1 = Math.min(Math.max(idxX(cx+r),0), width-1);

        int y0 = Math.min(Math.max(idxY(cy-r),0), height-1);
        int y1 = Math.min(Math.max(idxY(cy+r),0), height-1);
        //all cells inside the circle are inside cells [x0..x1, y0..y1]
        for (int y = y0; y<=y1; ++y){
            for (int x = x0; x<=x1; ++x){
            //TODO: some sells , for big radiuses, are guaranteed outside the circle. Perform the check.
                ArrayList<LocatedItem> bots = get(x,y);
                for(int i =0;i<bots.size();++i){
                    LocatedItem bot=bots.get(i);
                    double bx = bot.getX();
                    double by = bot.getY();
                    if (BBUtil.sqr(bx-cx)+BBUtil.sqr(by-cy)<=r2){
                        //if bot is really inside the cell
                        boolean doStop = callback.item(bot);
                        if (doStop)
                            return;//callback signalled to finish iteration
                    }
                }
            }
        }
    }
    
    public void remove(LocatedItem itm){
        ArrayList<LocatedItem> cell = get(idxX(itm.getX()), idxY(itm.getY()));
        int idx = cell.indexOf(itm);
        if (idx != -1){
            cell.remove(idx);
        }
   }
   /**remove dead items from cells*/
    /*
   public void removeDead(){
        int idx = 0;
        for (int y = 0; y<height; ++y){
            for (int x = 0; x<width; ++x,++idx){
                ArrayList<LocatedItem> cell = get(x,y);
                for (int i =0;i<cell.size();++i){
                    if (cell.get(i).isDead()){
                        cell.remove(i);
                        --i;
                    }
                }
            }
        }

        //update bots in the outside cells
        for(int i = 0; i<unaligned.size(); ++i){
            LocatedItem bot = unaligned.get(i);
            double bx = bot.getX();
            double by = bot.getY();
            if (bx >=cellx0 && bx <=cellx1 && by>=celly0 && by <=celly1){
                unaligned.remove(i);
                --i;
                putBot(bot);
            }
        }
  }
  */
    
}